package com.bestcem.xm.member.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.*;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.bestcem.bp.xm.user.grpc.v1.services.Organization;
import com.bestcem.xm.common.core.constant.AppRequestErrorCode;
import com.bestcem.xm.common.core.constant.AppResourceErrorCode;
import com.bestcem.xm.common.core.constant.RegTypes;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.domain.web.XmResultJson;
import com.bestcem.xm.common.core.sql.ColumnDesc;
import com.bestcem.xm.common.core.sql.ConditionDataConverter;
import com.bestcem.xm.common.core.sql.SQLConditionCompiler;
import com.bestcem.xm.common.core.uitls.EnumUtil;
import com.bestcem.xm.common.core.uitls.GRpcUtil;
import com.bestcem.xm.common.core.uitls.JsonUtil;
import com.bestcem.xm.componet.filestorage.service.StorageService;
import com.bestcem.xm.member.config.MemberSignConfig;
import com.bestcem.xm.member.constant.ColumnConstant;
import com.bestcem.xm.member.constant.Constants;
import com.bestcem.xm.member.controller.param.*;
import com.bestcem.xm.member.controller.v2.param.CountGroupMemberParam;
import com.bestcem.xm.member.controller.v2.param.FilterMembersParam;
import com.bestcem.xm.member.controller.v2.param.HttpDeleteGroupMemberParam;
import com.bestcem.xm.member.controller.vo.MemberVO;
import com.bestcem.xm.member.dao.*;
import com.bestcem.xm.member.entity.mongo.ExtendFieldValue;
import com.bestcem.xm.member.entity.mysql.MemberEntity;
import com.bestcem.xm.member.entity.mysql.SourceCardDo;
import com.bestcem.xm.member.entity.pojo.*;
import com.bestcem.xm.member.enums.*;
import com.bestcem.xm.member.grpc.client.UserClient;
import com.bestcem.xm.member.grpc.client.XmMemberClient;
import com.bestcem.xm.member.grpc.service.param.PageParam;
import com.bestcem.xm.member.grpc.service.param.extendfield.BatchUpdateFieldsParam;
import com.bestcem.xm.member.grpc.service.param.extendfield.UpdateEnumItemParam;
import com.bestcem.xm.member.grpc.service.param.extendfield.UpdateFieldParam;
import com.bestcem.xm.member.grpc.service.param.member.*;
import com.bestcem.xm.member.handler.member.IndividualMemberHandler;
import com.bestcem.xm.member.handler.member.InstitutionMemberHandler;
import com.bestcem.xm.member.handler.member.InteriorMemberHandler;
import com.bestcem.xm.member.listener.EasyExcelListener;
import com.bestcem.xm.member.mq.message.MemberMessage;
import com.bestcem.xm.member.service.*;
import com.bestcem.xm.member.service.dto.*;
import com.bestcem.xm.member.service.param.AddFootprintParam;
import com.bestcem.xm.member.service.param.DeliveryMembersDynamicSql;
import com.bestcem.xm.member.service.param.SelectMemberDynamicSql;
import com.bestcem.xm.member.util.FieldUtil;
import com.bestcem.xm.member.util.MemberUtil;
import com.bestcem.xm.member.util.RpcClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.bestcem.xm.member.constant.Constants.*;
import static com.bestcem.xm.member.enums.MemberTypeEnum.*;

/**
 * 示例服务
 *
 * @author yf.wang <yf.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/7/16
 */

@Slf4j
@Service
public class MemberServiceImpl implements MemberService {

    @Resource
    private MemberDao memberDao;

    @Resource
    private IndividualMemberHandler individualMemberHandler;

    @Resource
    private InstitutionMemberHandler institutionMemberHandler;

    @Resource
    private InteriorMemberHandler interiorMemberHandler;

    @Resource
    private ExtendFieldDao extendFieldDao;

    @Resource
    private ExtendFieldEnumItemDao extendFieldEnumItemDao;

    @Resource
    private DisplayRangeDao displayRangeDao;

    /*@Resource
    private OssService ossService;*/
    @Autowired
    private StorageService storageService;

    @Resource
    private SourceCardDao sourceCardDao;

    @Resource
    private GroupDao groupDao;

    @Resource
    private GroupService groupService;

    @Resource
    private SequenceService sequenceService;

    @Resource
    private FootprintServiceImpl footprintService;

    @Resource
    private XmMemberClient xmMemberClient;

    /**
     * 用户客户端
     */
    @Resource
    private UserClient userClient;

    @Resource
    private DisplayRangeService displayRangeService;

    @Resource
    private RpcClientUtil rpcClientUtil;

    @Resource
    private MemberUtil memberUtil;

    @Resource
    private ProjectDao projectDao;

    @Resource
    private MemberService memberService;

    @Resource
    private MemberUserGroupDao memberUserGroupDao;

    @Resource
    private MemberUserGroupService memberUserGroupService;

    @Resource
    private MemberSignConfig memberSignConfig;

    @Override
    public ServiceResult<Boolean> checkUserDisplayRangePerm(CheckDisplayRangePermParam param) {
        MemberDO member = memberDao.findById(param.getMemberId());
        if (member == null) {
            log.error("联系人不存在：{}", param);
            return ServiceResult.fail("联系人不存在");
        }

        DisplayRangeDO displayRange = displayRangeDao.findByOrgAndMemberType(param.getOrgId(), member.getMemberType());
        if (displayRange == null) {
            log.error("权限异常：{}", param);
            return ServiceResult.fail("权限异常");
        }

        // 所有人可见或者具有在可见人范围内
        boolean hasPerm = DisplayRangeTypeEnum.ALL.matches(displayRange.getType())
                || (StringUtils.hasText(displayRange.getRoleIds()) && displayRange.getRoleIds().contains(param.getUserId()));

        return ServiceResult.success(hasPerm);
    }

    @Override
    public ServiceResult<Boolean> checkUserDisplayRangePermByMemberType(CheckDisplayRangePermByMemberTypeParam param) {
        DisplayRangeDO displayRange = displayRangeDao.findByOrgAndMemberType(param.getOrgId(), param.getMemberTypeEnum().getIndex());
        if (displayRange == null) {
            log.error("权限异常：{}", param);
            return ServiceResult.fail("权限异常");
        }

        // 所有人可见或者具有在可见人范围内
        String roleIds = displayRange.getRoleIds();
        List<String> roleList = null;
        if (StrUtil.isNotEmpty(roleIds)) {
            roleList = JSON.parseArray(roleIds, String.class);
        }
        boolean hasPerm = DisplayRangeTypeEnum.ALL.matches(displayRange.getType())
                || (!CollectionUtils.isEmpty(roleList) && roleList.contains(param.getUserId()));

        return ServiceResult.success(hasPerm);
    }

    @Override
    public ServiceResult<String> createMember(HttpCreateMemberParam param) {
        // fixBug 20210917 只有个人联系人才限额
        if (param.getMemberType() == INDIVIDUAL.getIndex()) {
            // 只有个人联系人才检查剩余的可创建的联系人的数量
            ServiceResult<String> result = memberUtil.checkMemberNumber(param.getOrgId());
            if (!result.isSuccess()) {
                return result;
            }
        }

        // 扩展字段校验与转换
        if (CollectionUtil.isNotEmpty(param.getFieldValues())) {
            List<HttpMemberFieldParam> fieldValues = param.getFieldValues();
            for (HttpMemberFieldParam field : fieldValues) {
                // 对格式进行校验
                if (ObjectUtil.isNotEmpty(field.getValue())) {
                    ServiceResult<String> result1 = FieldUtil.checkFieldValueFormat(field);
                    if (!result1.isSuccess()) {
                        return ServiceResult.<String>fail(result1.getMsg()).setErrorCode("400");
                    }
                }
            }
        }
        param.setSource("manual");

        // 校验生日和年龄。如有生日，则年龄由该生日生成
        ServiceResult<String> serviceResult = checkBirthdayAndAge(param);
        if (!serviceResult.isSuccess()) {
            return serviceResult;
        }

        // 根据不同联系人选择不同的逻辑
        switch (EnumUtil.getEnumByIndex(MemberTypeEnum.class, param.getMemberType())) {
            case INDIVIDUAL:
                return individualMemberHandler.save(param);
            case INSTITUTION:
                return institutionMemberHandler.save(param);
            case INTERIOR:
                return interiorMemberHandler.save(param);
            default:
                return ServiceResult.<String>fail("联系人类型字段缺失")
                        .setErrorCode(String.valueOf(AppRequestErrorCode.PARAM_LOST));
        }
    }

    private ServiceResult<String> checkBirthdayAndAge(HttpMemberBaseParam param) {
        if (StringUtils.hasText(param.getBirthday())) {
            Date birthday;
            try {
                birthday = new SimpleDateFormat(ConditionDataConverter.DATE_FORMAT2).parse(param.getBirthday());
            } catch (ParseException e) {
                return ServiceResult.<String>fail("日期不合法")
                        .setErrorCode(String.valueOf(AppRequestErrorCode.PARAM_FORMAT));
            }
            Date currentTime = new Date();
            if (birthday.getTime() > currentTime.getTime()) {
                return ServiceResult.<String>fail("日期不能超过当前时间")
                        .setErrorCode(String.valueOf(AppRequestErrorCode.PARAM_RANGE));
            }
            int age = com.bestcem.xm.common.core.uitls.DateUtil.calculateAge(birthday, currentTime);
            param.setAge(age);
        }
        if (Objects.nonNull(param.getAge()) && param.getAge() > 200) {
            return ServiceResult.<String>fail("年龄超过上限")
                    .setErrorCode(String.valueOf(AppRequestErrorCode.PARAM_RANGE));
        }
        return ServiceResult.success();
    }

    @Override
    public ServiceResult<Integer> deleteMembers(HttpDeleteMembersParam param) {
        deleteMemberByIds(param.getMembers());
        List<MemberDO> members = memberDao.findByIds(param.getMembers());

        // 管理员不校验组织层级权限
        Integer adminSign = 1;
        boolean isAdmin = adminSign.equals(param.getSuperRole()) || adminSign.equals(param.getIsSenior());
        // 校验用户组织层级权限
        Set<String> storeIds = members.stream().map(MemberDO::getStoreId).filter(StringUtils::hasText).collect(Collectors.toSet());
        if (!isAdmin && !CollectionUtil.isEmpty(storeIds)) {
            ServiceResult<Collection<String>> userStoreCheckResult =
                    memberUserGroupService.checkUserGroupPermissions(param.getUserId(), storeIds);
            Collection<String> accessibleStoreIds;
            if (userStoreCheckResult.isSuccess()) {
                accessibleStoreIds = userStoreCheckResult.getData();
            } else {
                if (log.isErrorEnabled()) {
                    log.error("用户组织层级权限校验失败");
                }
                accessibleStoreIds = new ArrayList<>();
            }

            // 还原没有操作权限的联系人
            if (storeIds.size() > accessibleStoreIds.size()) {
                List<String> unAccessibleMemberIds = members.stream()
                        .filter(m -> !accessibleStoreIds.contains(m.getStoreId()))
                        .map(MemberDO::getId).collect(Collectors.toList());
                memberDao.updateStatusByIds(unAccessibleMemberIds, NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
                members = members.stream().filter(m -> !unAccessibleMemberIds.contains(m.getId())).collect(Collectors.toList());
            }
        }

        // 删除机构联系人的项目信息
        List<String> insMemberIds = members.stream()
                .filter(m -> INSTITUTION.getIndex() == m.getMemberType())
                .map(MemberDO::getId).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(insMemberIds)) {
            projectDao.deleteByMemberIds(param.getOrgId(), insMemberIds);
        }

        // 获取组织信息
        ServiceResult<OrganizationDTO> organization =
                rpcClientUtil.getOrganization(param.getOrgId());
        if (!organization.isSuccess()) {
            return ServiceResult.<Integer>fail("调用服务失败").setErrorCode("-1");
        }
        OrganizationDTO data = organization.getData();
        if (CollUtil.isNotEmpty(members)) {
            members.forEach(member -> {
                // 推送删除的联系人
                MemberMessage message = MemberUtil.memberDoToMemberMessage(member);
                message.setOrgCode(data.getCode());
                message.setOrgName(data.getName());
                String identityType = "";
                switch (EnumUtil.getEnumByIndex(MemberTypeEnum.class, member.getMemberType())) {
                    case INSTITUTION:
                        message.setIdentityType("企业对接人");
                        message.setNumberId("000");
                        break;
                    case INTERIOR:
                        identityType = ObjectUtil.isNotEmpty(member.getEmployeeStatus()) ? EMPLOYEE_STATUS_MAP.get(member.getEmployeeStatus()) : "";
                        message.setIdentityType(identityType);
                        break;
                    case INDIVIDUAL:
                        identityType = memberUtil.getIdentityType(member.getPhone(), param.getOrgId(), member.getEmail());
                        message.setIdentityType(identityType);
                        break;
                }
                memberUtil.publishUpdateMember(message);
            });
        }
        return ServiceResult.success(null, "Batch delete member finish");
    }

    @Override
    public ServiceResult<Integer> deleteGroupMembers(HttpDeleteGroupMemberParam param) {
        // 获取组织信息
        ServiceResult<OrganizationDTO> orgResult = rpcClientUtil.getOrganization(param.getOrgId());
        OrganizationDTO data = orgResult.getData();
        if (!orgResult.isSuccess()) {
            log.error("获取组织信息失败:{}", orgResult.getMsg());
            return ServiceResult.fail("获取组织信息失败");
        }

        FilterMembersParam filterMembersParam = new FilterMembersParam();
        filterMembersParam.setOrgId(param.getOrgId());
        filterMembersParam.setMType(param.getMemberType());
        filterMembersParam.setMemberIds(param.getMembers());
        filterMembersParam.setGroupId(param.getGroupId());
        filterMembersParam.setSearch(param.getSearch());
        filterMembersParam.setCondition(param.getCondition());
        filterMembersParam.setUserId(param.getUserId());
        filterMembersParam.setSuperRole(param.getSuperRole());
        filterMembersParam.setIsSenior(param.getIsSenior());
        filterMembersParam.setExcludeCount(true);

        // 软删除联系人
        List<String> ids = null;
        int size = 1000;
        int num = 0;
        filterMembersParam.setPage(0);
        filterMembersParam.setRowsPerPage(size);
        do {
            ServiceResult<PageDTO<MemberDO>> membersResult = this.baseFilterMembers(filterMembersParam);
            if (!membersResult.isSuccess()) {
                return ServiceResult.fail(membersResult.getMsg());
            }

            PageDTO<MemberDO> pageMembers = membersResult.getData();
            List<MemberDO> members = pageMembers.getRows();
            ids = members.stream().map(MemberDO::getId).collect(Collectors.toList());
            // 异步处理后续流程
            if (CollUtil.isNotEmpty(ids)) {
                memberDao.updateStatusByIds(ids, NewMemberStatusEnum.MEMBER_STATUS_SOFT_DELETE.getIndex());
                num += ids.size();
                memberUtil.publishDeleteMember(ids, data, param.getOrgId());
            }
        } while (CollectionUtil.isNotEmpty(ids));

        return ServiceResult.success(num);
    }

    @Override
    public ServiceResult<Integer> deleteMembers(DeleteMembersParam param) {
        int deleteCount = deleteMemberByIds(param.getIds());
        return ServiceResult.success(deleteCount);
    }

    /**
     * 修改联系人扩展字段
     *
     * @param param 批量更新参数
     * @return
     */
    @Override
    public ServiceResult<Boolean> batchUpdateExtendField(BatchUpdateFieldsParam param) {
        if (!CollectionUtils.isEmpty(param.getDeleteFieldIds())) {
            // 删除联系人扩展字段
            memberDao.batchDeleteExtendFields(param.getOrgId(), param.getMemberTypeEnum().getIndex(), param.getDeleteFieldIds());
        }

        // 更新扩展字段
        List<UpdateFieldParam> updateFieldParams = param.getUpdateFieldParams();

        if (CollectionUtils.isEmpty(updateFieldParams)) {
            return ServiceResult.success(Boolean.TRUE);
        }

        List<String> updateEnumIds = new LinkedList<>();
        Map<String, String> fieldIdAndNameMap = new HashMap<>(updateFieldParams.size());

        for (UpdateFieldParam updateFieldParam : updateFieldParams) {
            fieldIdAndNameMap.put(updateFieldParam.getId(), updateFieldParam.getName());

            // 枚举类型扩展字段才需要更新枚举选项
            if (!ExtendFieldTypeEnum.ENUM.equals(updateFieldParam.getExtendFieldTypeEnum())) {
                continue;
            }

            if (!CollectionUtils.isEmpty(updateFieldParam.getUpdateEnumItemParams())) {
                updateEnumIds.addAll(updateFieldParam.getUpdateEnumItemParams().stream().map(UpdateEnumItemParam::getId).collect(Collectors.toList()));
            }

            if (!CollectionUtils.isEmpty(updateFieldParam.getDeleteEnumItemIds())) {
                updateEnumIds.addAll(updateFieldParam.getDeleteEnumItemIds());
            }
        }

        // 修改联系人字段名
        List<ExtendFieldDO> updateFields = new ArrayList<>();
        if (!CollectionUtils.isEmpty(fieldIdAndNameMap)) {
            List<ExtendFieldDO> extendFieldDOs = extendFieldDao.findByIds(fieldIdAndNameMap.keySet());
            for (ExtendFieldDO extendFieldDO : extendFieldDOs) {
                String newName = fieldIdAndNameMap.get(extendFieldDO.getFieldId());
                if (extendFieldDO.getName() == null || !extendFieldDO.getName().equals(newName)) {
                    extendFieldDO.setName(newName);
                    updateFields.add(extendFieldDO);
                }
            }
            memberDao.batchUpdateFieldName(param.getOrgId(), param.getMemberTypeEnum().getIndex(), updateFields);
        }

        List<ExtendFieldEnumItemDO> enumItems = extendFieldEnumItemDao.findEnumsByIds(updateEnumIds);
        if (CollectionUtils.isEmpty(enumItems)) {
            return ServiceResult.success(Boolean.TRUE);
        }

        Map<String, ExtendFieldEnumItemDO> enumItemMap = enumItems.stream().collect(Collectors.toMap(e -> e.getExtendFieldId() + e.getFieldEnumId(), Function.identity()));
        List<ExtendFieldEnumItemDO> extendFieldEnumItemDOS = new ArrayList<>();
        for (UpdateFieldParam updateFieldParam : updateFieldParams) {
            // 枚举类型扩展字段才需要更新枚举选项
            if (!ExtendFieldTypeEnum.ENUM.equals(updateFieldParam.getExtendFieldTypeEnum())) {
                continue;
            }

            // 更新联系人枚举选项值
            if (!CollectionUtils.isEmpty(updateFieldParam.getUpdateEnumItemParams())) {
                for (UpdateEnumItemParam ue : updateFieldParam.getUpdateEnumItemParams()) {
                    ExtendFieldEnumItemDO currentEnumItem = enumItemMap.get(updateFieldParam.getId() + ue.getId());
                    if (currentEnumItem != null && !ue.getName().equals(currentEnumItem.getName())) {
                        currentEnumItem.setFieldEnumId(currentEnumItem.getName());
                        currentEnumItem.setName(ue.getName());
                        extendFieldEnumItemDOS.add(currentEnumItem);
                    }
                }
            }

            // 删除联系人枚举选项值
            if (!CollectionUtils.isEmpty(updateFieldParam.getDeleteEnumItemIds())) {
                for (String enumItemId : updateFieldParam.getDeleteEnumItemIds()) {
                    ExtendFieldEnumItemDO currentEnumItem = enumItemMap.get(updateFieldParam.getId() + enumItemId);
                    if (currentEnumItem != null) {
                        currentEnumItem.setFieldEnumId(currentEnumItem.getName());
                        currentEnumItem.setName("");
                        extendFieldEnumItemDOS.add(currentEnumItem);
                    }
                }
            }
        }

        if (!CollectionUtils.isEmpty(extendFieldEnumItemDOS)) {
            memberDao.updateMemberFieldEnumValue(param.getOrgId(), param.getMemberTypeEnum().getIndex(), extendFieldEnumItemDOS);
        }

        // 当存在更新个人联系人扩展字段时，更新个人联系人分组信息
        if (!CollectionUtils.isEmpty(updateFields)
                || !CollectionUtils.isEmpty(param.getDeleteFieldIds())
                && INDIVIDUAL.equals(param.getMemberTypeEnum())) {
            groupService.sendGroupMessages(param.getOrgId());
        }

        return ServiceResult.success(Boolean.TRUE);
    }

    /**
     * 根据 id 列表 软删除 联系人
     *
     * @param ids
     */
    private int deleteMemberByIds(List<String> ids) {
        // 软删除 更新相关联系人状态为
        Long deleteCount = memberDao.updateStatusByIds(ids, NewMemberStatusEnum.MEMBER_STATUS_SOFT_DELETE.getIndex());
        return deleteCount.intValue();
    }

    @Override
    public ServiceResult<MemberDTO> getMemberDetail(String id, String orgId, String userId, Integer superAdmin, Integer seniorAdmin) {
        // 根据 id 获取联系人信息
        MemberDO member = memberDao.findById(id);
        if (ObjectUtil.isEmpty(member)) {
            return ServiceResult.<MemberDTO>fail(StrUtil.format("Member[id={}] not found", id)).setErrorCode(String.valueOf(AppResourceErrorCode.NOT_FOUND));
        }

        // 管理员不校验
        Integer adminSign = 1;
        boolean isAdmin = adminSign.equals(superAdmin) || adminSign.equals(seniorAdmin);
        // 校验用户组织层级权限
        if (!isAdmin && StringUtils.hasText(member.getStoreId())) {
            ServiceResult<Boolean> userStoreCheckResult =
                    memberUserGroupService.checkUserGroupPermission(userId, member.getStoreId());
            if (!userStoreCheckResult.isSuccess() || !Boolean.TRUE.equals(userStoreCheckResult.getData())) {
                if (log.isErrorEnabled()) {
                    log.error("用户:{}层级权限异常", userId);
                }
                return ServiceResult.fail(userStoreCheckResult.getMsg());
            }
        }

        Boolean isDisplayContact = displayRangeService.isDisplayContact(orgId, member.getMemberType(), userId);

        // 构造联系人详情数据
        MemberDTO memberDTO = ConvertUtil.parseMember(isDisplayContact, member);
        // 根据store 获取storeName 与 ancestors
        if (StrUtil.isNotBlank(memberDTO.getStore())) {
            // 获取storeName
            GroupDTO groupDTO = userClient.getGroup(memberDTO.getStore());
            memberDTO.setStoreName(StrUtil.isNotEmpty(groupDTO.getTitle()) ? groupDTO.getTitle() : "");
            //获取层级结构
            ServiceResult<List<GroupNodeDTO>> listServiceResult =
                    xmMemberClient.listAncestorGroups(memberDTO.getStore());
            if (listServiceResult.isSuccess()) {
                List<GroupNodeDTO> data = listServiceResult.getData();
                if (CollUtil.isNotEmpty(data)) {
                    List<GroupDTO> ancestorDTOs = data.stream()
                            // 考虑到获取祖先层级时，服务端会把其自身返回回来，所以这里要过滤
                            .filter(groupNodeDTO -> !memberDTO.getStore().equals(groupNodeDTO.getId()))
                            .map(d -> {
                                GroupDTO dto = new GroupDTO();
                                dto.setCode(d.getCode());
                                dto.setId(d.getId());
                                dto.setSeq(d.getSeq());
                                dto.setTitle(d.getTitle());
                                return dto;
                            }).collect(Collectors.toList());
                    // 添加门店信息
                    ancestorDTOs.add(groupDTO);
                    memberDTO.setAncestors(ancestorDTOs);
                }
            }
        }
        List<ExtendFieldDTO> fieldValList = memberDTO.getFieldValList();
        if (CollUtil.isNotEmpty(fieldValList)) {
            List<String> ids = fieldValList.stream().map(ExtendFieldDTO::getId).collect(Collectors.toList());
            // 获取db中所有的扩展字段
            List<ExtendFieldDO> extendFields = extendFieldDao.findByIds(ids);
            // 根据id进行分组
            Map<String, List<ExtendFieldDO>> extendFieldMap =
                    extendFields.stream().collect(Collectors.groupingBy(ExtendFieldDO::getFieldId));
            List<ExtendFieldDTO> newFieldValList = new ArrayList<>();

            if (CollUtil.isNotEmpty(extendFields)) {
                fieldValList.forEach(var -> {
                    if (extendFieldMap.containsKey(var.getId())) {
                        // 如果包含了该数据
                        ExtendFieldDO dbExtendField = extendFieldMap.get(var.getId()).get(0);
                        ExtendFieldDTO dto = new ExtendFieldDTO();
                        dto.setName(dbExtendField.getName());
                        dto.setValue(var.getValue());
                        dto.setType(var.getType());
                        dto.setId(var.getId());
                        newFieldValList.add(dto);
                    }
                });
            }
            memberDTO.setFieldValList(newFieldValList);
        }
        return ServiceResult.success(memberDTO);
    }

    @Override
    public ServiceResult<MemberDTO> getMemberDetail(GetMemberParam param) {
        MemberDO memberDO = memberDao.findById(param.getId());
        if (Objects.nonNull(memberDO)) {
            return ServiceResult.success(ConvertUtil.memberDoToMemberDto(memberDO));
        }
        return ServiceResult.fail(String.format("memberId[%s] not found", param.getId()));
    }

    @Override
    public ServiceResult<PageDTO<MemberDTO>> listFilterDeliveryMembers(ListFilterMemberParam param) {
        DeliveryMembersDynamicSql dynamicSql = new DeliveryMembersDynamicSql();
        dynamicSql.setOrgId(param.getOrgId());
        dynamicSql.setMemberType(param.getMemberTypeEnum().getIndex());
        dynamicSql.setDeliveryType(param.getDeliveryTypeEnum().getIndex());
        dynamicSql.setStatus(NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());
        dynamicSql.setAppId(param.getAppId());
        // 模糊搜索
        String search = param.getRegex();
        if (StrUtil.isNotEmpty(search)) {
            // 去空格 + 转义正则关键字
            search = ReUtil.escape(search.trim());
            dynamicSql.setSearch(search);
        }

        // condition 解析成条件
        if (ObjectUtil.isNotEmpty(param.getCondition())) {
            Map<String, ColumnDesc> columnMapping = new HashMap<>(8);
            columnMapping.put("mobile", ColumnDesc.newInstance("phone"));
            columnMapping.put("store", ColumnDesc.newInstance("store_id", ColumnDesc.JDBC_TYPE_BINARY));
            columnMapping.put("openid", ColumnDesc.newInstance("open_id"));
            columnMapping.put("appid", ColumnDesc.newInstance("app_id"));
            columnMapping.put("alipay_user_id", ColumnDesc.newInstance("ali_pay_user_id"));
            columnMapping.put("institut_id", ColumnDesc.newInstance("institute_id"));
            columnMapping.put("institut_name", ColumnDesc.newInstance("institute_name"));
            columnMapping.put("employeeID", ColumnDesc.newInstance("employee_id"));

            ServiceResult<Wrapper<MemberEntity>> result = SQLConditionCompiler.newInstance(columnMapping)
                    .compile(JsonUtil.tryConvertToJSONObject(param.getCondition()));
            if (!result.isSuccess()) {
                return ServiceResult.fail("查询条件解析失败");
            }
            dynamicSql.setWrapper(result.getData());
        }

        // 管理员不校验联系人组织层级
        Integer adminSign = 1;
        boolean isAdmin = adminSign.equals(param.getSuperRole()) || adminSign.equals(param.getIsSenior());
        dynamicSql.setAdmin(isAdmin);
        if (!isAdmin) {
            // 组织层级权限校验
            ServiceResult<Collection<String>> userStoreIdsResult = memberUserGroupService.userGroupIds(param.getUserId());
            if (!userStoreIdsResult.isSuccess()) {
                if (log.isErrorEnabled()) {
                    log.error("查询用户组织层级失败({}):{}", param.getUserId(), userStoreIdsResult);
                }
                // 查询用户组织层级失败时，展示无组织联系人
                userStoreIdsResult.setData(new ArrayList<>());
            }
            Collection<String> userDirectGroupIds = userStoreIdsResult.getData();
            // 查询组织层级叶子节点、失效节点数量,如果小于阈值，那么单表查询
            Integer leafAndInactiveStoreCount = memberUserGroupDao.countSubAndInactiveGroup(param.getOrgId(), userDirectGroupIds);
            if (leafAndInactiveStoreCount < Constants.STORE_IN_SEARCH_MAX_COUNT) {
                List<MemberUserGroupDO> memberStores = memberUserGroupDao.allSubAndInactiveGroup(param.getOrgId(), userDirectGroupIds);
                List<String> storeIds = memberStores.stream().map(MemberUserGroupDO::getGroupId).collect(Collectors.toList());
                dynamicSql.setUserLeafGroupIds(storeIds);
            } else {
                // 自然增序排序
                String userGroupIds = userDirectGroupIds.stream().sorted().collect(Collectors.joining("\",\"", "[\"", "\"]"));
                dynamicSql.setUserGroupIds(userGroupIds);
            }
        }

        Integer count = memberDao.countDeliveryMembers(dynamicSql);
        if (count < 1) {
            return ServiceResult.success(new PageDTO<>(new ArrayList<>(), count));
        }

        // 分页
        PageParam page = param.getParam();
        if (page != null && page.getPage() != null && page.getSize() != null) {
            int start = PageUtil.getStart(page.getPage() - 1, page.getSize());
            dynamicSql.setStart(start);
            dynamicSql.setSize(page.getSize());
        }
        List<MemberDO> members = memberDao.listDeliveryMembers(dynamicSql);

        // 根据可见性,隐藏或显示手机号和邮箱
        Boolean isDisplayContact = displayRangeService.isDisplayContact(param.getOrgId(), param.getMemberTypeEnum().getIndex(), param.getUserId());
        List<MemberDTO> memberDTOS = members.stream().map(o -> convertByIsDisplay(o, isDisplayContact)).collect(Collectors.toList());

        return ServiceResult.success(new PageDTO<>(memberDTOS, count));
    }

    @Override
    public ServiceResult<Long> countIndividualMember(String orgId) {
        Long indCount = memberDao.countIndividualMember(orgId);
        return ServiceResult.success(indCount);
    }

    @Override
    public XmResultJson<String> updateMember(HttpUpdateHttpMemberParam param) {

        // 获取可见权限
        Boolean isDisplayContact = displayRangeService.isDisplayContact(param.getOrgId(), param.getMemberType(), param.getUserId());
        param.setIsDisplayContact(isDisplayContact);

        // 根据id 获取该联系人
        MemberDO member = memberDao.findById(param.getId());

        // 联系人是否存在
        if (member == null) {
            if (log.isErrorEnabled()) {
                log.error("联系人不存在：{}", param.getId());
            }
            return XmResultJson.fail(AppRequestErrorCode.PARAM_RANGE, "联系人不存在");
        }

        // 管理员不校验
        Integer adminSign = 1;
        boolean isAdmin = adminSign.equals(param.getSuperRole()) || adminSign.equals(param.getIsSenior());
        // 校验用户组织层级权限
        if (!isAdmin && StringUtils.hasText(member.getStoreId())) {
            ServiceResult<Boolean> userStoreCheckResult =
                    memberUserGroupService.checkUserGroupPermission(param.getUserId(), member.getStoreId());
            if (!userStoreCheckResult.isSuccess() || !Boolean.TRUE.equals(userStoreCheckResult.getData())) {
                if (log.isErrorEnabled()) {
                    log.error("用户:{}层级权限异常", param.getUserId());
                }
                return XmResultJson.fail(AppRequestErrorCode.PARAM_RANGE, userStoreCheckResult.getMsg());
            }
        }

        // 不可见的时候不容许保存mobile和email(包括扩展字段),所以和原来保持一致
        if (!isDisplayContact) {
            param.setMobile(member.getPhone());
            param.setEmail(member.getEmail());
            // 扩展字段设置为旧值
            if (StrUtil.isNotEmpty(member.getFieldValList())
                    && CollUtil.isNotEmpty(param.getFieldValues())) {
                // string 转 ExtendFieldValue
                List<ExtendFieldValue> extendFieldValues =
                        JSONObject.parseArray(member.getFieldValList(), ExtendFieldValue.class);
                Map<String, HttpMemberFieldParam> map = param.getFieldValues().stream()
                        .filter(o -> StrUtil.isNotEmpty(o.getId()))
                        .collect(Collectors.toMap(HttpMemberFieldParam::getId, o -> o));
                // 数据库没有的,不需要设为旧值
                for (ExtendFieldValue oldVal : extendFieldValues) {
                    HttpMemberFieldParam inputVal = map.get(oldVal.getFieldId());
                    if (Objects.nonNull(inputVal) && Objects.nonNull(inputVal.getType())) {
                        if (ExtendFieldTypeEnum.MOBILE.matches(inputVal.getType())
                                || ExtendFieldTypeEnum.EMAIL.matches(inputVal.getType())) {
                            inputVal.setValue(Optional.ofNullable(oldVal.getFieldValue()).orElse("").toString());
                        }
                    }
                }
            }
        }

        // 校验生日和年龄。如有生日，则年龄由该生日生成
        ServiceResult<String> serviceResult = checkBirthdayAndAge(param);
        if (!serviceResult.isSuccess()) {
            return XmResultJson.fail(Integer.valueOf(serviceResult.getErrorCode()), serviceResult.getMsg());
        }

        // 根据不同联系人选择不同的逻辑
        switch (EnumUtil.getEnumByIndex(MemberTypeEnum.class, member.getMemberType())) {
            case INDIVIDUAL:
                // 个人联系人
                return individualMemberHandler.updateManualMember(member, param);
            case INSTITUTION:
                // 机构联系人
                return institutionMemberHandler.updateManualMember(member, param);
            case INTERIOR:
                // 内部联系人
                return interiorMemberHandler.updateManualMember(member, param);
            default:
                return XmResultJson.fail(-1, "联系人类型不匹配");
        }
    }

    @Override
    public ServiceResult<String> updateMember(UpdateMemberParam param) {
        return null;
    }


    @Override
    public XmResultJson<JSONObject> validateEmail(String orgId, Integer mType, String email) {
        MemberDO member = memberDao.findByOrgIdAndFieldAndMemberType(orgId, "email", email, mType);
        JSONObject result = new JSONObject();
        result.put("is_valid", ObjectUtil.isEmpty(member));
        return XmResultJson.success(result);
    }

    @Override
    public XmResultJson<JSONObject> validateMobile(String orgId, Integer mType, String mobile) {
        MemberDO member = memberDao.findByOrgIdAndFieldAndMemberType(orgId, "phone", mobile, mType);
        JSONObject result = new JSONObject();
        result.put("is_valid", ObjectUtil.isEmpty(member));
        return XmResultJson.success(result);
    }

    @Override
    public XmResultJson<List<MetaFieldDTO>> listComputeFields() {
        return XmResultJson.success(COMPUTES_INFO);
    }

    @Override
    public XmResultJson<ListMetaFieldsDTO> listMetaFields(Integer mtype, String orgId) {
        ListMetaFieldsDTO dto = new ListMetaFieldsDTO();
        switch (mtype) {
            case 0:
                // 个人联系人
                dto.setFields(INDIV_INFO);
                dto.setExtendFields(getFieldByAttributeAndOrgId(mtype, orgId));
                break;
            case 1:
                //机构联系人
                dto.setFields(INSTITUT_INFO);
                dto.setExtendFields(getFieldByAttributeAndOrgId(mtype, orgId));
                break;
            case 2:
                //内部联系人
                dto.setFields(INNER_INFO);
                dto.setExtendFields(getFieldByAttributeAndOrgId(mtype, orgId));
                break;
            case 3:
                //项目
                dto.setFields(PROJECT_BASE_INFO);
                dto.setProjectFields(PROJECT_INFO);
                dto.setExtendFields(getFieldByAttributeAndOrgId(M_FIELD_ATTRIBUTE_INSTITUTION, orgId));
                dto.setProjectExtendFields(getFieldByAttributeAndOrgId(mtype, orgId));
                break;
            default:
                dto.setFields(null);
        }

        // 获取扩展字段列表
        return XmResultJson.success(dto);
    }

    @Override
    public ServiceResult<PageDTO<MemberDTO>> listByNameRegex(@Pattern(regexp = RegTypes.OBJECT_ID, message = "组织id格式错误") String orgId,
                                                             @Pattern(regexp = RegTypes.OBJECT_ID, message = "用户id格式错误") String userId,
                                                             @Nullable MemberTypeEnum memberTypeEnum,
                                                             @Nullable String nameRegex,
                                                             @NotNull PageParam pageParam,
                                                             @NotNull Integer superRole,
                                                             @NotNull Integer isSenior) {
        Integer memberType = memberTypeEnum == null ? null : memberTypeEnum.getIndex();
        FilterMembersParam param = new FilterMembersParam();
        param.setOrgId(orgId);
        param.setUserId(userId);
        param.setSuperRole(superRole);
        param.setIsSenior(isSenior);
        param.setMType(memberType);
        if (StringUtils.hasText(nameRegex)) {
            Map<String, String> regexs = new HashMap<>();
            regexs.put(ColumnConstant.Member.NAME, nameRegex);
            param.setRegexTargets(regexs);
        }
        param.setPage(pageParam.getPage());
        param.setRowsPerPage(pageParam.getSize());

        ServiceResult<PageDTO<MemberDO>> pageResult = this.baseFilterMembers(param);
        if (!pageResult.isSuccess()) {
            log.error("联系人导出异常:{}", pageResult.getMsg());
            return ServiceResult.fail("联系人导出异常");
        }
        PageDTO<MemberDO> pageMembers = pageResult.getData();
        Integer count = pageMembers.getTotal();
        List<MemberDO> members = pageMembers.getRows();
        if (CollectionUtils.isEmpty(members)) {
            return ServiceResult.success(new PageDTO<>(new ArrayList<>(), count));
        }

        ServiceResult<List<String>> result = xmMemberClient.listOrgAdminIds(orgId);
        if (!result.isSuccess()) {
            if (log.isErrorEnabled()) {
                log.error("查询用户管理员信息失败：orgId({}) : {}", orgId, result.getMsg());
            }
            return ServiceResult.fail(result.getMsg());
        }

        Set<Integer> memberTypes = members.stream().map(MemberDO::getMemberType).collect(Collectors.toSet());
        Map<Integer, Boolean> displayMap = new HashMap<>(memberTypes.size());
        for (Integer type : memberTypes) {
            if (type == null) {
                continue;
            }
            // 查询展示范围
            DisplayRangeDO displayRange = displayRangeDao.findByOrgAndMemberType(orgId, type);
            if (displayRange == null) {
                continue;
            }

            if (DisplayRangeTypeEnum.ADMIN.matches(type) &&
                    (!StringUtils.hasText(displayRange.getRoleIds())
                            || !result.getData().contains(userId))) {
                displayMap.put(type, Boolean.FALSE);
            }

            displayMap.put(type, Boolean.TRUE);
        }

        // 数据转化
        List<MemberDTO> memberDTOS = new ArrayList<>(members.size());
        for (MemberDO member : members) {
            boolean displayContact = Boolean.TRUE.equals(displayMap.get(member.getMemberType()));
            member.setEmail(MemberUtil.handleEmail(member.getEmail(), displayContact));
            member.setPhone(MemberUtil.handleMobile(member.getPhone(), displayContact));
            memberDTOS.add(this.convertFrom(member));
        }

        return ServiceResult.success(new PageDTO<>(memberDTOS, count));
    }

    /**
     * 获取扩展字段
     *
     * @param mType
     * @param orgId
     * @return
     */
    private List<MetaExtendFieldDTO> getFieldByAttributeAndOrgId(Integer mType, String orgId) {
        List<ExtendFieldDO> extendFields = extendFieldDao.listMemberExtendField(orgId, mType);
        if (CollUtil.isNotEmpty(extendFields)) {
            return extendFields.stream().map(field -> {
                MetaExtendFieldDTO dto = new MetaExtendFieldDTO();
                BeanUtil.copyProperties(field, dto);
                dto.setId(field.getFieldId());
                dto.setSource("info");
                if (field.getType() <= ExtendFieldTypeEnum.DATETIME.getIndex()) {
                    dto.setTypeName(FIELD_VALUE_TYPE.get(field.getType()));
                }
                if (field.getType().equals(ExtendFieldTypeEnum.AREA.getIndex()) ||
                        field.getType().equals(ExtendFieldTypeEnum.MOBILE.getIndex()) ||
                        field.getType().equals(ExtendFieldTypeEnum.EMAIL.getIndex())) {
                    dto.setTypeName(FIELD_VALUE_TYPE.get(0));
                } else if (field.getType().equals(ExtendFieldTypeEnum.ENUM.getIndex())) {
                    dto.setTypeName(FIELD_VALUE_TYPE.get(3));
                }
                return dto;
            }).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }


    @Override
    public ServiceResult<PageDTO<MemberDTO>> listMembers(ListContactMembersParam param) {
        ListFilterMemberParam filterMemberParam = new ListFilterMemberParam();
        filterMemberParam.setOrgId(param.getOrgId());
        filterMemberParam.setUserId(param.getUserId());
        filterMemberParam.setMemberTypeEnum(param.getMemberTypeEnum());
        filterMemberParam.setDeliveryTypeEnum(param.getDeliveryTypeEnum());
        filterMemberParam.setRegex(param.getRegex());
        filterMemberParam.setSuperRole(param.getSuperRole());
        filterMemberParam.setIsSenior(param.getIsSenior());
        filterMemberParam.setParam(param.getPageParam());
        filterMemberParam.setAppId(param.getAppId());
        return this.listFilterDeliveryMembers(filterMemberParam);
    }

    @Override
    public MemberDO queryMember(String orgId, Integer attribute, String field, String value) {
        return memberDao.findByOrgIdAndFieldAndMemberType(orgId, field, value, attribute);
    }

    @Override
    public MemberDO findWechatMember(String orgId, Integer attribute, String appId, String openId) {
        return memberDao.findByAppIdAndOpenId(orgId, attribute, appId, openId);
    }

    @Override
    public ServiceResult<Void> createImportMember(MemberDO member, Integer quota) {
        try {
            switch (EnumUtil.getEnumByIndex(MemberTypeEnum.class, member.getMemberType())) {
                case INDIVIDUAL:
                    // 个人联系人
                    return individualMemberHandler.saveImportMember(member, quota);
                case INSTITUTION:
                    // 机构联系人
                    return institutionMemberHandler.saveImportMember(member, quota);
                case INTERIOR:
                    // 内部联系人
                    return interiorMemberHandler.saveImportMember(member, quota);
                default:
                    return ServiceResult.success();
            }
        } catch (Exception e) {
            log.error("excel导入联系人创建异常", e);
        }
        return ServiceResult.fail("excel导入联系人创建异常");
    }

    @Override
    public ServiceResult<Void> updateImportMember(MemberDO member) {
        try {
            switch (EnumUtil.getEnumByIndex(MemberTypeEnum.class, member.getMemberType())) {
                case INDIVIDUAL:
                    // 个人联系人
                    return individualMemberHandler.updateImportMember(member);
                case INSTITUTION:
                    // 机构联系人
                    return institutionMemberHandler.updateImportMember(member);
                case INTERIOR:
                    // 内部联系人
                    return interiorMemberHandler.updateImportMember(member);
                default:
                    return ServiceResult.success();
            }
        } catch (Exception e) {
            log.error("excel导入联系人更新异常", e);
        }
        return ServiceResult.fail("excel导入联系人创建异常");
    }

    @Override
    public ServiceResult<PageDTO<MemberDO>> baseFilterMembers(FilterMembersParam param) {
        SelectMemberDynamicSql dynamicSql = new SelectMemberDynamicSql();
        dynamicSql.setOrgId(param.getOrgId());
        dynamicSql.setMemberType(param.getMType());
        dynamicSql.setMemberIds(param.getMemberIds());
        dynamicSql.setStatus(NewMemberStatusEnum.MEMBER_STATUS_ACTIVATED.getIndex());

        //搜索字段
        if (StrUtil.isNotEmpty(param.getEqSearch())) {
            dynamicSql.setEqSearch(param.getEqSearch());
        }
        // 模糊搜索
        String search = param.getSearch();
        if (StrUtil.isNotEmpty(search)) {
            // 去空格 + 转义正则关键字
            search = ReUtil.escape(search.trim());
            dynamicSql.setSearch(search);
        }

        // 分组搜索
        if (StringUtils.hasText(param.getGroupId())) {
            GroupDO groupDO = groupDao.findById(param.getGroupId());
            if (groupDO != null) {
                dynamicSql.setGroupSeq(groupDO.getSeq());
            }
        }

        // condition 解析成条件
        if (ObjectUtil.isNotEmpty(param.getCondition())) {
            Map<String, ColumnDesc> columnMapping = new HashMap<>(8);
            columnMapping.put("mobile", ColumnDesc.newInstance("phone"));
            columnMapping.put("store", ColumnDesc.newInstance("store_id", ColumnDesc.JDBC_TYPE_BINARY));
            columnMapping.put("openid", ColumnDesc.newInstance("open_id"));
            columnMapping.put("appid", ColumnDesc.newInstance("app_id"));
            columnMapping.put("alipay_user_id", ColumnDesc.newInstance("ali_pay_user_id"));
            columnMapping.put("institut_id", ColumnDesc.newInstance("institute_id"));
            columnMapping.put("institut_name", ColumnDesc.newInstance("institute_name"));
            columnMapping.put("employeeID", ColumnDesc.newInstance("employee_id"));

            ServiceResult<Wrapper<MemberEntity>> result = SQLConditionCompiler.newInstance(columnMapping)
                    .compile(JsonUtil.tryConvertToJSONObject(param.getCondition()));
            if (!result.isSuccess()) {
                return ServiceResult.fail("查询条件解析失败");
            }
            dynamicSql.setWrapper(result.getData());
        }

        // 管理员不校验联系人组织层级
        Integer adminSign = 1;
        boolean isAdmin = adminSign.equals(param.getSuperRole()) || adminSign.equals(param.getIsSenior());
        dynamicSql.setAdmin(isAdmin);
        if (!isAdmin) {
            // 组织层级权限校验
            ServiceResult<Collection<String>> userStoreIdsResult = memberUserGroupService.userGroupIds(param.getUserId());
            if (!userStoreIdsResult.isSuccess()) {
                if (log.isErrorEnabled()) {
                    log.error("查询用户组织层级失败({}):{}", param.getUserId(), userStoreIdsResult);
                }
                // 查询用户组织层级失败时，展示无组织联系人
                userStoreIdsResult.setData(new ArrayList<>());
            }
            Collection<String> userDirectGroupIds = userStoreIdsResult.getData();

            // 查询组织层级叶子节点、失效节点数量,如果小于阈值，那么单表查询
            Integer leafAndInactiveStoreCount = memberUserGroupDao.countSubAndInactiveGroup(param.getOrgId(), userDirectGroupIds);
            if (leafAndInactiveStoreCount < Constants.STORE_IN_SEARCH_MAX_COUNT) {
                // 单表查询
                List<MemberUserGroupDO> memberStores = memberUserGroupDao.allSubAndInactiveGroup(param.getOrgId(), userDirectGroupIds);
                List<String> userLeafGroupIds = memberStores.stream().map(MemberUserGroupDO::getGroupId).collect(Collectors.toList());
                dynamicSql.setUserLeafGroupIds(userLeafGroupIds);
            } else {
                // 自然增序排序
                String userGroupIds = userDirectGroupIds.stream().sorted().collect(Collectors.joining("\",\"", "[\"", "\"]"));
                dynamicSql.setUserGroupIds(userGroupIds);
            }
        }

        Integer count = 0;
        if (!param.isExcludeCount()) {
            count = memberDao.countByDynamicSql(dynamicSql);
        }

        if (param.isExcludeData() || (!param.isExcludeCount() && count < 1)) {
            return ServiceResult.success(new PageDTO<>(new ArrayList<>(), count));
        }

        dynamicSql.setDesc(param.getDescending());
        dynamicSql.setOrderBy(param.getSortBy());

        // 分页
        Integer page = param.getPage();
        Integer size = param.getRowsPerPage();
        if (page != null && size != null) {
            int start = PageUtil.getStart(page - 1, size);
            dynamicSql.setStart(start);
            dynamicSql.setSize(size);
        }

        List<MemberDO> members = memberDao.filterMembersByDynamicSql(dynamicSql);
        return ServiceResult.success(new PageDTO(members, count));

    }

    @Override
    public XmResultJson<PageDTO> filterMembers(FilterMembersParam param) {
        ServiceResult<PageDTO<MemberDO>> membersResult = this.baseFilterMembers(param);
        if (!membersResult.isSuccess()) {
            return XmResultJson.fail(-1, membersResult.getMsg());
        }

        PageDTO<MemberDO> pageMembers = membersResult.getData();
        List<MemberDO> members = pageMembers.getRows();
        Integer count = pageMembers.getTotal();
        //获取可视范围
        Boolean displayContact =
                displayRangeService.isDisplayContact(param.getOrgId(), param.getMType(), param.getUserId());

        List<MemberVO> memberDTOS = new ArrayList<>();
        // 获取门店id与名称的映射
        Map<String, JSONObject> storeMap = memberUtil.getStoreMap(param.getOrgId());
        if (CollUtil.isNotEmpty(members)) {
            members.forEach(member -> {
                MemberVO dto = ConvertUtil.memberDoToMemberVo(member);
                // 设置可视范围
                dto.setMobile(MemberUtil.handleMobile(dto.getMobile(), displayContact));
                dto.setEmail(MemberUtil.handleEmail(dto.getEmail(), displayContact));
                // 设置门店名称与组织名称
                JSONObject obj = storeMap.get(member.getStoreId());
                if (Objects.nonNull(obj)) {
                    dto.setStoreName(obj.getString("name"));
                    dto.setOrgName(obj.getString("name"));
                }
                if (StrUtil.isNotEmpty(member.getFieldValList())) {
                    List<ExtendFieldValue> extendFieldValues = JSONArray.parseArray(member.getFieldValList(), ExtendFieldValue.class);
                    if (CollUtil.isNotEmpty(extendFieldValues)) {
                        List<ExtendFieldDTO> extendFieldDTOS = extendFieldValues.stream().map(field -> {
                            // 获取扩展字段类型
                            Integer type = field.getFieldType();
                            Object fieldValue = field.getFieldValue();
                            ExtendFieldDTO dto1 = new ExtendFieldDTO();
                            dto1.setId(field.getFieldId());
                            dto1.setType(field.getFieldType());
                            dto1.setName(field.getFieldName());
                            if (ObjectUtil.isNotEmpty(fieldValue)) {
                                String value = String.valueOf(fieldValue);
                                // 获取扩展字段值
                                switch (ExtendFieldTypeEnum.getByIndex(type)) {
                                    case DATETIME:
                                        if (StrUtil.isNotEmpty(value)) {
                                            DateTime parse = DateUtil.parse(value);
                                            dto1.setValue(parse.toString("yyyy/MM/dd"));
                                        }
                                        break;
                                    // 扩展标签进行隐藏手机和邮箱的处理
                                    case MOBILE:
                                        dto1.setValue(MemberUtil.handleMobile(value, displayContact));
                                        break;
                                    case EMAIL:
                                        dto1.setValue(MemberUtil.handleEmail(value, displayContact));
                                        break;
                                    default:
                                        dto1.setValue(value);
                                }
                            }
                            return dto1;
                        }).collect(Collectors.toList());
                        dto.setFieldValList(extendFieldDTOS);
                    }
                } else {
                    dto.setFieldValList(new ArrayList<>());
                }
                memberDTOS.add(dto);
            });
        }
        PageDTO pageDTO = new PageDTO();
        pageDTO.setTotal(count);
        pageDTO.setRows(memberDTOS);
        return XmResultJson.success(pageDTO);
    }

    @Override
    public List<MemberDO> findByOrgIdAndInstituteNameAndStatusNot(String orgId, String instituteName, Integer status) {
        return memberDao.findByOrgIdAndInstituteNameAndStatusNot(orgId, instituteName, status);
    }

    @Override
    public MemberDO findOneByNameAndMtypeAndStatusNe(String name, int mType, int status) {
        return memberDao.findOneByNameAndMtypeAndStatusNe(name, mType, status);
    }

    @Override
    public ServiceResult<List<String>> importDeliverMember(ImportDeliverMemberParam param) {
        // 下载投放上传的联系人excel文件
        //String filePath = ossService.getFilePrefix() + "deliver_member/" + param.getDeliverId().trim() + "/投放名单.xlsx";
        String filePath = storageService.getPropertiesValueBy(null, "base-path") + "deliver_member/" + param.getDeliverId().trim() + "/投放名单.xlsx";
        log.info("读取文件:{}", filePath);
        List<MemberDO> members;
        try {
            members = this.parseInputStream(filePath);
        } catch (Exception e) {
            log.error("[Member] 获取oss文件流失败:url {}", e.getMessage());
            return ServiceResult.fail(e.getMessage());
        }

        log.info("导入文件为:{}", members);
        if (CollectionUtils.isEmpty(members)) {
            log.error("导入文件为空:{}", filePath);
            return ServiceResult.fail("导入文件为空");
        }

        // 查询门店信息
        ServiceResult<Map<String, String>> storeResult = xmMemberClient.getStoreNameAndIdMap(param.getOrgId());
        log.info("门店信息：{}", storeResult.getData());
        if (!storeResult.isSuccess()) {
            log.error("查询门店信息异常:{}", storeResult.getMsg());
            return ServiceResult.fail("查询门店信息异常");
        }

        List<String> memberIds = new ArrayList<>(members.size());
        for (MemberDO member : members) {
            member.setOrgId(param.getOrgId());
            member.setMemberType(INDIVIDUAL.getIndex());
            member.setSource("excel");
            String storeId = org.apache.commons.lang3.StringUtils.trim(member.getStoreId());
            member.setStoreId(Optional.ofNullable(storeResult.getData().get(storeId)).orElse(""));

            MemberDO upsertMember;
            // 查询非软删除联系人是否存在
            if ("mobile".equals(param.getPrimaryKey())) {
                upsertMember = memberDao.findUndeletedMemberByPhone(member.getOrgId(), INDIVIDUAL.getIndex(), member.getPhone());
            } else {
                upsertMember = memberDao.findUndeletedMemberByEmail(member.getOrgId(), INDIVIDUAL.getIndex(), member.getEmail());
            }
            log.info("查询联系人：{}", upsertMember);

            // 创建/更新联系人来源名片
            SourceCardDo card = new SourceCardDo();
            card.setOrgId(param.getOrgId());
            card.setSource(0);
            card.setData(JSONObject.toJSONString(member));

            // 创建/更新联系人
            if (upsertMember != null) {
                log.info("更新联系人：,{}", upsertMember);
                upsertMember.setName(member.getName());
                upsertMember.setStoreId(member.getStoreId());
                upsertMember.setPhone(member.getPhone());
                upsertMember.setEmail(member.getEmail());
                upsertMember.setUpdateTime(com.bestcem.xm.common.core.uitls.DateUtil.getCommonDate());
                // 更新联系人邮箱、store、name、source
                member.setGroupSyncToken(null);
                memberDao.updateMember(upsertMember, false);
                // 更新来源名片
                // 查询联系人来源名片数量 这里更新时也增加，与之前保持一致
                String nextCardNo = "00".concat(sourceCardDao.count(param.getOrgId(), upsertMember.getId()) + 1 + "");
                card.setSerial(upsertMember.getMemberNo() + "-" + nextCardNo.substring(nextCardNo.length() - 3));
                card.setMemberId(upsertMember.getId());
                card.setData(JSONObject.toJSONString(upsertMember));
                sourceCardDao.upsert(card);
            } else {
                log.info("创建联系人：,{}", member);
                // 查询个人联系人序号
                ServiceResult<Integer> sequenceResult = sequenceService.getSequence("member:memberNo_INDIVIDUAL_" + param.getOrgId(), 1L);
                member.setMemberNo("C" + NumberUtil.decimalFormat("00000000", sequenceResult.getData()));

                // 创建联系人
                member.setCreateTime(com.bestcem.xm.common.core.uitls.DateUtil.getCommonDate());
                member.setUpdateTime(member.getCreateTime());
                member.setStatus(com.bestcem.xm.member.enums.MemberStatusEnum.ACTIVATED.getIndex());
                upsertMember = memberDao.deliverSaveMember(member);

                // 创建来源名片
                // 查询联系人来源名片数量 这里更新时也增加，与之前保持一致
                card.setSerial(upsertMember.getMemberNo() + "-001");
                card.setMemberId(upsertMember.getId());
                card.setData(JSONObject.toJSONString(upsertMember));
                sourceCardDao.save(card);

                // 推送创建联系人消息至足迹点
                this.sendFootprintCreateMember(upsertMember);
            }
            log.info("发送msg创建联系人：,{}", member);
            memberIds.add(upsertMember.getId());

            // 推送创建/更新联系人消息至BI
            this.sendBiCreateMember(upsertMember);
        }
        // 异步更新联系人分组信息
        groupService.sendGroupMessages(param.getOrgId());
        memberIds = memberIds.stream().distinct().collect(Collectors.toList());
        log.info("投放联系人：{}", members);
        return ServiceResult.success(memberIds);
    }

    /**
     * 推送创建的联系人信息至BI
     *
     * @param createdMember 已创建的联系人
     */
    private void sendBiCreateMember(MemberDO createdMember) {
        Organization organization = userClient.getOrganization(createdMember.getOrgId());
        //MemberMessage message = new MemberMessage();
        //log.info("推送创建的联系人信息至BI createdMember:{}", JSONObject.toJSONString(createdMember));
        //BeanUtil.copyProperties(createdMember, message);
        MemberMessage message = BeanUtil.toBean(createdMember, MemberMessage.class);
        message.setOrgCode(organization.getCode());
        message.setOrgName(organization.getName());
        message.setIdentityType("非员工");
        if (StringUtils.hasText(createdMember.getPhone())) {
            MemberDO innerMember = memberDao.findByOrgIdAndFieldAndMemberType(createdMember.getOrgId(), "phone", createdMember.getPhone(), INTERIOR.getIndex());
            if (innerMember == null && StringUtils.hasText(createdMember.getEmail())) {
                innerMember = memberDao.findByOrgIdAndFieldAndMemberType(createdMember.getOrgId(), "email", createdMember.getEmail(), INTERIOR.getIndex());
            }
            if (innerMember != null) {
                EmployeeStatusEnum employeeStatusEnum = EnumUtil.getEnumByIndex(EmployeeStatusEnum.class, innerMember.getEmployeeStatus());
                if (employeeStatusEnum == null) {
                    message.setIdentityType("");
                    log.error("内部联系人状态异常：{}", innerMember);
                } else {
                    message.setIdentityType(employeeStatusEnum.getName());
                }
            }
        }

        // 发送消息
        memberUtil.publishCreateMember(message);
    }

    /**
     * 推送创建的联系人信息至足迹
     *
     * @param createdMember 已创建的联系人
     */
    private void sendFootprintCreateMember(MemberDO createdMember) {
        JSONObject footprint = new JSONObject();
        footprint.put("title", "成为联系人");
        AddFootprintParam addFootprintParam = new AddFootprintParam();
        addFootprintParam.setOrgId(createdMember.getOrgId());
        addFootprintParam.setMemberId(createdMember.getId());
        addFootprintParam.setType(FootprintTypeEnum.MEMBER.getIndex());
        addFootprintParam.setSourceKey(createdMember.getId());
        addFootprintParam.setContent(footprint);
        addFootprintParam.setFootprintTime(createdMember.getCreateTime());
        log.info("发送创建足迹点：{}", addFootprintParam);
        footprintService.publishFootprint(addFootprintParam);
    }

    private List<MemberDO> parseInputStream(String excelFilePath) {
        EasyExcelListener<DeliverMemberDTO> excelListener = new EasyExcelListener<>();
        //InputStream is = ossService.downloadFile(excelFilePath);
        //InputStream is = storageService.downloadFile(excelFilePath);
        InputStream is = new ByteArrayInputStream(storageService.downloadByUrl(storageService.getPropertiesValueBy(null, "domain") + excelFilePath).bytes());
        // 读取数据
        EasyExcel.read(is, DeliverMemberDTO.class, excelListener).sheet().doRead();
        if (CollectionUtils.isEmpty(excelListener.getResult())) {
            return new ArrayList<>(0);
        }

        List<MemberDO> members = new ArrayList<>(excelListener.getResult().size());
        for (DeliverMemberDTO dto : excelListener.getResult()) {
            MemberDO member = new MemberDO();
            members.add(member);
            member.setName(dto.getName());
            member.setPhone(dto.getPhone());
            member.setEmail(dto.getEmail());
            if (!StringUtils.hasText(dto.getPhone())) {
                member.setPhone(dto.getAnotherPhone());
            }
            if (!StringUtils.hasText(dto.getEmail())) {
                member.setEmail(dto.getAnotherEmail());
            }
            member.setStoreId(dto.getStore());
        }
        return members;
    }

    @Override
    public ServiceResult<MemberDTO> getMemberById(GetMemberByIdParam param) {
        // 根据 id 获取联系人信息
        MemberDO member = memberDao.findById(param.getId());
        if (ObjectUtil.isNotEmpty(member)) {
            return ServiceResult.success(convertFrom(member));
        }
        return ServiceResult.success();
    }

    @Override
    public ServiceResult<List<MemberDTO>> listMembersByIds(ListMemberByIdsParam param) {
        List<MemberDTO> memberDTOS = new ArrayList<>();
        List<MemberDO> members = memberDao.findByIds(param.getIds());
        if (CollUtil.isNotEmpty(members)) {
            List<MemberDTO> memberDTOList =
                    members.stream().map(this::convertFrom).collect(Collectors.toList());
            return ServiceResult.success(memberDTOList);
        }
        return ServiceResult.success(memberDTOS);
    }

    @Override
    public ServiceResult<List<MemberDTO>> listDeliveryMembersByIds(ListDeliveryMembersByIdsParam param) {
        // 参数校验
        Set<ConstraintViolation<Object>> violations = GRpcUtil.validateParam(param);
        if (!CollectionUtils.isEmpty(violations)) {
            ConstraintViolation<Object> violation = violations.iterator().next();
            log.warn("参数异常：{}", param);
            return ServiceResult.fail(violation.getMessage());
        }

        // 查询联系人列表
        List<MemberDO> members = memberDao.list(param.getOrgId(), param.getMemberIds());
        if (CollectionUtils.isEmpty(members)) {
            log.warn("联系人不存在：{}", param);
            return ServiceResult.success(new ArrayList<>());
        }

        // 根据可见性,隐藏或显示手机号和邮箱
        Boolean isDisplayContact = displayRangeService.isDisplayContact(param.getOrgId(), param.getMemberTypeEnum().getIndex(), param.getUserId());

        // 数据转化
        List<MemberDTO> memberDTOS = new ArrayList<>(members.size());
        for (MemberDO member : members) {
            MemberDTO memberDTO = this.convertByIsDisplay(member, isDisplayContact);
            memberDTOS.add(memberDTO);
        }

        return ServiceResult.success(memberDTOS);
    }

    @Override
    public ServiceResult<List<MemberDTO>> listMembersByParam(ListMembersByParamParam paramParam) {
        JSONObject paramJson = new JSONObject();
        String param = paramParam.getParam();
        try {
            if (StrUtil.isNotEmpty(param)) {
                paramJson = JSONObject.parseObject(param);
            }
        } catch (Exception e) {
            log.error("{} 解析json失败", param, e);
            return ServiceResult.fail("参数格式错误");
        }
        List<MemberDO> memberDOS = memberDao.findByParam(covertParamToDocument(paramJson));
        if (CollUtil.isNotEmpty(memberDOS)) {
            List<MemberDTO> memberDTOS = memberDOS.stream().map(memberDo ->
                    ConvertUtil.memberDoToMemberDto(memberDo)).collect(Collectors.toList());
            return ServiceResult.success(memberDTOS);
        }
        return ServiceResult.success(new ArrayList<>());
    }

    @Override
    public String getMemberNo(String orgId, Integer attribute) {
        switch (EnumUtil.getEnumByIndex(MemberTypeEnum.class, attribute)) {
            case INDIVIDUAL:
                // 个人联系人
                return individualMemberHandler.getMemberNo(orgId, attribute);
            case INSTITUTION:
                // 机构联系人
                return institutionMemberHandler.getMemberNo(orgId, attribute);
            case INTERIOR:
                // 内部联系人
                return interiorMemberHandler.getMemberNo(orgId, attribute);
        }
        return "";
    }

    @Override
    public ServiceResult<CreateMembersDTO> createMembers(List<HttpCreateMemberParams> memberParams, int index, String orgId, String userId) {
        List<String> ids = new ArrayList<>();
        List<List<Object>> errs = new ArrayList<>();
        if (CollUtil.isNotEmpty(memberParams)) {
            memberParams.forEach(memberParam -> {
                HttpCreateMemberParam param = new HttpCreateMemberParam();
                String instituteName = StrUtil.isNotEmpty(memberParam.getInstituteName()) ? memberParam.getInstituteName() : "";
                param.setInstituteName(instituteName);
                param.setOrgId(orgId);
                String name = StrUtil.isNotEmpty(memberParam.getName()) ? memberParam.getName() : "";
                param.setName(name);
                String mobile = StrUtil.isNotEmpty(memberParam.getMobile()) ? memberParam.getMobile() : "";
                param.setMobile(mobile);
                String email = StrUtil.isNotEmpty(memberParam.getEmail()) ? memberParam.getEmail() : "";
                param.setEmail(email);
                param.setMemberType(memberParam.getMType());
                param.setUserId(userId);
                param.setLabels(new ArrayList<>());
                param.setFieldValues(new ArrayList<>());
                param.setAddress(new ArrayList<>());
                ServiceResult<String> result = createMember(param);
                if (result.isSuccess()) {
                    ids.add(result.getData());
                } else {
                    List<Object> err = new ArrayList<>();
                    err.add(memberParam);
                    err.add(result.getMsg());
                    errs.add(err);
                }
            });
        }
        CreateMembersDTO dto = new CreateMembersDTO();
        dto.setIds(ids);
        dto.setErrs(errs);
        return ServiceResult.success(dto);
    }

    /**
     * @param paramJson
     * @return
     */
    private QueryWrapper covertParamToDocument(JSONObject paramJson) {
        QueryWrapper queryWrapper = new QueryWrapper();
        if (!paramJson.isEmpty()) {
            Set<String> keys = paramJson.keySet();
            keys.forEach(key -> {
                switch (key) {
                    case "id":
                        String id = paramJson.getString(key);
                        queryWrapper.eq("member_id", id);
                        break;
                    case "status":
                        JSONArray status = paramJson.getJSONArray(key);
                        queryWrapper.in("status", status.toJavaList(String.class));
                        break;
                    default:
                        queryWrapper.eq(key, paramJson.getString(key));
                }
            });
        }
        return queryWrapper;
    }

    private MemberDTO convertFrom(MemberDO member) {
        MemberDTO memberDTO = ConvertUtil.memberDoToMemberDto(member);
        if (StrUtil.isNotEmpty(member.getFieldValList())) {
            List<ExtendFieldValue> extendFieldValues = JSONArray.parseArray(member.getFieldValList(), ExtendFieldValue.class);
            if (CollUtil.isNotEmpty(extendFieldValues)) {
                List<ExtendFieldDTO> extendFieldDTOS = extendFieldValues.stream().map(field -> {
                    ExtendFieldDTO dto = new ExtendFieldDTO();
                    dto.setName(field.getFieldName());
                    dto.setValue(Optional.ofNullable(field.getFieldValue()).orElse("").toString());
                    dto.setType(field.getFieldType());
                    dto.setId(field.getFieldId());
                    return dto;
                }).collect(Collectors.toList());
                memberDTO.setFieldValList(extendFieldDTOS);
            }
        }
        return memberDTO;
    }

    /**
     * 根据是否展示联系方式,对特定数据做处理,并进行EO转DTO操作
     *
     * @param member           联系人集合持久层对象
     * @param isDisplayContact 是否展示联系方式
     * @return com.bestcem.xm.member.service.dto.MemberDTO
     * @author zhangfeng <feng.zhang@idiaoyan.com>
     * @date 2021/8/29 11:03
     */
    private MemberDTO convertByIsDisplay(MemberDO member, boolean isDisplayContact) {
        MemberDTO dto = ConvertUtil.memberDoToMemberDto(member);
        // 根据可见范围 处理返回值的手机号和邮箱隐藏敏感信息
        dto.setMobile(MemberUtil.handleMobile(dto.getMobile(), isDisplayContact));
        dto.setEmail(MemberUtil.handleEmail(dto.getEmail(), isDisplayContact));

        // 处理扩展字段的手机号和邮箱
        if (CollUtil.isNotEmpty(dto.getFieldValList()) && isDisplayContact) {
            for (ExtendFieldDTO extendField : dto.getFieldValList()) {
                if (ExtendFieldTypeEnum.MOBILE.matches(extendField.getType())) {
                    extendField.setValue(MemberUtil.handleMobile(extendField.getValue(), isDisplayContact));
                }
                if (ExtendFieldTypeEnum.EMAIL.matches(extendField.getType())) {
                    extendField.setValue(MemberUtil.handleEmail(extendField.getValue(), isDisplayContact));
                }
            }
        }
        return dto;
    }

    @Override
    public ServiceResult<Long> countOrgMember(CountOrgMemberParam param) {
        Long count = memberDao.countOrgMember(param.getOrgId(), param.getMemberType());
        return ServiceResult.success(count);
    }

    @Override
    public ServiceResult<Integer> countGroupMember(CountGroupMemberParam param) {
        FilterMembersParam filterMembersParam = new FilterMembersParam();
        filterMembersParam.setOrgId(param.getOrgId());
        filterMembersParam.setMType(param.getMemberType());
        filterMembersParam.setGroupId(param.getGroupId());
        filterMembersParam.setUserId(param.getUserId());
        filterMembersParam.setSuperRole(param.getSuperRole());
        filterMembersParam.setIsSenior(param.getIsSenior());
        filterMembersParam.setExcludeData(Boolean.TRUE);

        ServiceResult<PageDTO<MemberDO>> membersResult = this.baseFilterMembers(filterMembersParam);
        if (!membersResult.isSuccess()) {
            if (log.isErrorEnabled()) {
                log.error("查询用户分组数量异常:{}", membersResult.getMsg());
            }
            return ServiceResult.fail("查询用户分组数量异常");
        }

        return ServiceResult.success(membersResult.getData().getTotal());
    }

    @Override
    public List<MemberDO> findByOrgIdAndMemberTypeAndInstituteNameAndStatusNot(String orgId, int memberType, String instituteName, int status) {
        return memberDao.findByOrgIdAndMemberTypeAndInstituteNameAndStatusNot(orgId, memberType, instituteName, status);
    }

    @Override
    public ServiceResult<Boolean> checkUserStorePermission(CheckUserStorePermParam param) {
        // 管理员不校验组织层级权限
        Integer adminSign = 1;
        boolean isAdmin = adminSign.equals(param.getSuperRole()) || adminSign.equals(param.getIsSenior());
        if (!isAdmin) {
            return memberUserGroupService.checkUserGroupPermission(param.getUserId(), param.getStoreId());
        }
        return ServiceResult.success(true);
    }

    @Override
    public ServiceResult<List<MemberDTO>> listMembersByWx(ListMembersByWxParam param) {
        List<MemberDTO> memberDTOS = new ArrayList<>();
        List<MemberQuery> queryParams = param.getQueryParams();
        List<MemberDO> members =
                memberDao.findByWx(param.getMemberType(), param.getOrgId(), param.getStatus(), queryParams);
        if (CollUtil.isNotEmpty(members)) {
            List<MemberDTO> memberDTOList =
                    members.stream().map(this::convertFrom).collect(Collectors.toList());
            return ServiceResult.success(memberDTOList);
        }
        return ServiceResult.success(memberDTOS);

    }

    /*@Override
    public ServiceResult<String> genSig(String memberId) {
        String signKey = memberSignConfig.getSubscribeKey();
        Map<String, Object> map = new HashMap<>(1);
        map.put("memberId", memberId);
        return ServiceResult.success(SignUtil.genSign(map, signKey));
    }*/
}
